#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File:binary_to_pic.py
@Author:Josco
@Date:2022/09/24 7:27:55
@Version:1.0
@Description: 对文件进行二进制转换后转为二维码, 并添加文件名字、文件类型以及hash校验等信息
'''
import numpy as np
import time
import cv2 as cv
from hash_check import hash_check
import os
from modify_time import *


def read_bits_from_file(file_name):
    """
    param:
        filename: 文件名
    return:
        data_bits: 将一个文件变成bits的一个np列表
    """
    data_bytes = np.fromfile(file_name, dtype = "uint8")
    data_bits = np.unpackbits(data_bytes)
    # print(type(data_bits))
    return data_bits

def read_binarydata(binary_data,width, height):
    """
    desc: 将一维数组的bits流list转为映射到[0-3]空间的二维list, 并完成相邻三个数组作为RGB通道的数组压缩操作
    param:
        binary_data : 二进制文件, 是一个list
        width: 最终成像像素的宽, height: 最终成像像素的高
    return:
        binary_data_list: reshape和转换后的二维数组, 数组中的数据范围在[0-3]
        pic_num: 根据数据可以形成width * height的图像张数
        remain_dat: 填充了多少个0
    """
    len_binary_data = len(binary_data) #原始二进制长度
    # 因为需要两位两位组合成一个点，同时需要满足3个点合并为一个点后生成N 个widht *height高的图片，所以需要计算需添加的0的长度
    remain_data = height * width *2 - len_binary_data % (width * height *2)
    # print(remain_data)
    if remain_data:
        remain_data_np = np.zeros((int(remain_data)), dtype="uint8") # 生成N个需要填充数据的nplsit
        binary_data = np.hstack((binary_data,remain_data_np)) # 将两个nplist合并
    binary_data = binary_data.reshape(-1,2) # reshape成二维数组
    binary_data = np.mat(binary_data) * np.mat([[2],[1]]) # 将reshape成二维数组后的两个一组的数据映射到0-3空间内
    binary_data = binary_data.reshape(-1,width) # reshape成width宽
    pic_num = int(len(binary_data) / height) # 计算生成的照片张数
    # print(pic_num)
    return binary_data, pic_num, remain_data

def get_file_info(file_name, pic_num, height, width,n, remain_data):
    """
    desc: 加载文件的详细信息，返回一个拼接好的字符串
    """
    # 开始获取文件详细信息
    file_info = os.stat(file_name)
    cTime = formatTime(file_info.st_ctime)  # 创建时间
    mTime = formatTime(file_info.st_mtime)  # 修改时间
    aTime = formatTime(file_info.st_mtime)  # 访问时间
    offset = (0, 1, 2)  # 偏移的秒数（不知道干啥的）
    # 添加文件信息
    check_info = file_name + " " + str(cTime)+ " " + str(mTime)+ " " + str(aTime)+ " " + str(width) + " " + str(height) + " " + str(n)+ " " + str(remain_data) + " " + str(pic_num)
    return check_info

def pic_by_data(check_info, binary_data, pic_num, height, width, n = 1, top_bottom = 3, left_right = 3):
    """
    desc: 基于binary_data进行画图, 为n*n个像素代表一个点, 并将hash结果还有一些文件信息等校验信息加入到开头
    """
    do_hash_check = hash_check() # 调用hash校验库

    final_binary_mat = [] # 初始化最终写成img的所有矩阵的数组
    # 便利pic_num张图像的数据
    for i in range(pic_num):
        local_binary_data = np.array(binary_data[i*height:(i+1)*height]) # 获取当前的pic块
        # 添加md5校验信息
        # print(local_binary_data)
        # print(local_binary_data.shape)
        hash_data = do_hash_check.md5(str(local_binary_data)) # 计算当前块的hash值
        # jiezhidaozhe作为尾部校验信息，当解码时前两行的数据转为str类型后遇到jiezhidaozhe则证明基本信息和校验信息就到这里了
        check_info_data = check_info + " " + str(i) + " " + hash_data + " " + '1234321'# 结合文件基本信息、当前第几张图像和校验信息最后还有尾部信息
        # print(check_info_data)
        # print(local_binary_data)
        check_info_data_bit = do_hash_check.str2bitarray(check_info_data) # 将信息转为bitlist
        # print(check_info_data_bit)
        check_info_data_bit = np.array(list(check_info_data_bit), dtype="uint8") # 转为nparray

        # 校验信息默认占两行，一行01长度为width*2,两行的长度为width*4，当前校验信息长度小于width*4，大概在900个0或1的长度，所以就不加判断了
        check_info_remain_data = (width * 4) - len(check_info_data_bit) # 看校验信息需要添加多少个0
        check_info_remain_data_np = np.zeros((int(check_info_remain_data)), dtype="uint8")
        check_info_data_bit = np.hstack((check_info_data_bit,check_info_remain_data_np)) # 把校验信息的bitarray和需添加的多少个0加起来
        
        # 对前2行所有校验数据进行映射和reshape操作
        check_info_data_bit = check_info_data_bit.reshape(-1,2) # 将校验信息bits reshape成（width * 4 + width * 2）/2行，2列的二维数组
        check_info_data_bit = np.mat(check_info_data_bit) * np.mat([[2],[1]]) #映射到0-3空间
        check_info_data_bit = np.array(check_info_data_bit)
        check_info_data_bit = check_info_data_bit.reshape(2,width) #将校验信息reshape成2行width宽的数组

        local_binary_data = np.concatenate((check_info_data_bit, local_binary_data), axis=0) # 拼接校验信息和文件编码信息
        local_binary_data = local_binary_data.reshape(-1, int(width/3),3) #转为3维数组

        # # 设置单张伪二维码图像上下左右黑边设置
        # top_bottom = 3 #设置上下的黑线宽度
        # left_right = 3 # 设置左右的黑线宽度
        
        top_bottom_mat = np.zeros((top_bottom,local_binary_data.shape[1], 3), dtype = "uint8") # 创建top_bottom行，width列的3维矩阵作为黑边
        local_binary_data = np.concatenate((top_bottom_mat, local_binary_data, top_bottom_mat), axis=0) # 先添加顶部和底部的黑边
        # print(local_binary_data.shape)
        left_right_mat = np.zeros((local_binary_data.shape[0],left_right, 3), dtype = "uint8") ## 创建height行，left_right列的3维矩阵
        local_binary_data = np.concatenate((left_right_mat, local_binary_data, left_right_mat), axis=1) # 后添加左部和右部的黑边

        # # 按照放大倍数（n倍）的方式存储数据
        # h,w,c = local_binary_data.shape
        # local_binary_data1 = np.tile(local_binary_data, (n, n)) #宽高都复制n遍
        # local_binary_data1 = local_binary_data1.reshape(-1, w * n, 3) # reshape成合理的宽高
        # cv.imwrite("./data2/" + str(i) + ".bmp", local_binary_data1 * 85) # 存为图像
        
        final_binary_mat.append(local_binary_data) # 将每张图像的03区间的数据添加到final_binary_mat中

    # 因为是3张图片合成一个图片, 所以当pic_num不是3的倍数的时候, 需要额外添加纯黑色的mat, 用于最后一张图像的合成
    if pic_num % 3==0:
        print("当%3余0的时候不需要添加mat")
    elif pic_num % 3 == 1:
        print("当%3余1的时候需要添加两个mat")
        append_mat = np.zeros(local_binary_data.shape,dtype="uint8")
        final_binary_mat.append(append_mat)
        final_binary_mat.append(append_mat) #连续加两个mat
    else:
        print("当%3余2的时候需要添加一个mat")
        final_binary_mat.append(np.zeros(local_binary_data.shape, dtype="uint8"))
    
    # 按照三张合成一张照片的方式合成照片并按照n的大小进行显示
    for j in range(int(len(final_binary_mat)/3)):
        # 将邻近的三个图片合为一张图片
        local_binary_data = np.concatenate((final_binary_mat[j*3], final_binary_mat[j*3+1], final_binary_mat[j*3+2]),axis=1)
        # 把数组中的0，1，2，3映射为0，85，170，255
        local_binary_data = local_binary_data * 85
        pic_name = "./data1/" + str(j) + "++.bmp"
        cv.imwrite(pic_name, local_binary_data) # 存为图像
        # show_pic(pic_name, n) # 按照预设定的帧率进行播放, 我这里预设定的帧率为50帧每秒

    return None

def show_pic(pic_name,n = 1, wait_key = 100):
    """
    desc: 将生成的图像挨个展示, 按10帧率的频率进行播放图像, 即1000/10
    param:
    pic_name : 文件路径
    n: 图像从bmp*n倍显示
    wait_key: 一张图片显示在屏幕的时间
    return:
        NONE
    """
    image_data = cv.imread(pic_name)
    h, w, c = image_data.shape
    resize_width = int(w * n)
    resize_height = int(h * n)
    windowname = "Image"    # 使用中文会乱码
    cv.namedWindow(windowname, cv.WINDOW_NORMAL)
    cv.resizeWindow(windowname, resize_width, resize_height)
    cv.imshow(windowname, image_data)
    cv.waitKey(wait_key)
    # cv.destroyAllWindows()

    return None

def main():
    time_start = time.time()
    file_name = "./data/21.pdf"
    # 读取文件的bits list
    binary_data = read_bits_from_file(file_name)
    # 存储n*n表示一个点时对应的图像的width和height, 第一位是n，第二位是width,第三位是height,使用展示长宽为840*1260来进行计算width = 840/n*3,height = 1260/n
    # index_list = [[3,840,420], [4,630,315],[5,504,252],[6,420,210],[7,360,180]]
    index_list = [[5,420,240],[6,360,200],[7,360,200]]
    # 当前使用的n,widht,和宽, n * n个像素代表一个点, 通过修改index_list[n]的参数来调整图像到底是由n*n个像素表示为一个点
    local_list = index_list[0]    
    n, width, height = local_list[0], local_list[1],local_list[2]
    # 对二进制文件进行读取、分块并填充0
    binary_data_list, pic_num, remain_data = read_binarydata(binary_data,width, height)
    # 加载文件详细信息
    check_info = get_file_info(file_name, pic_num, height, width, n, remain_data)
    # 画图
    pic_by_data(check_info, binary_data_list, pic_num, height, width, n)

    time_end = time.time()
    time_sum = time_end - time_start
    print(time_sum)
    

if __name__ == '__main__':
    main()